﻿/* Autor: Achim Schnell */

using System;
using System.Reflection;

namespace AppFx.ComponentModel.Descriptive
{
	/// <summary>
	/// Ei PropertyDescriptor, der vorkompilierte Accessoren
	/// zum schnellen Zugriff auf Eigenschaftswerte verwendet.
	/// </summary>
	public class CompiledPropertyDescriptor : PropertyDescriptorEx
	{
		#region Properties

		/// <summary>
		/// Gibt oder setzt den Accessor, der zum Zugriff auf die Werte der Eigenschaft benutzt wird.
		/// </summary>
		protected IDynamicFieldAccessor Accessor { get; set; }

		/// <summary>
		/// Ruft einen Wert ab, der angibt, ob Wertänderungsbenachrichtigungen für diese Eigenschaft von außerhalb des Eigenschaftendeskriptors stammen dürfen.
		/// </summary>
		/// <returns>true, wenn Wertänderungsbenachrichtigungen von außerhalb des Eigenschaftendeskriptors stammen dürfen, andernfalls false.</returns>
		public override bool SupportsChangeEvents
		{
			get
			{
				return true;
			}
		}

		#endregion

		#region Constructors

		/// <summary>
		/// Erstellt eine neue Instanz der <see cref="CompiledPropertyDescriptor"/>-Klasse.
		/// </summary>
		/// <param name="componentType">Typ der Komponente deren Eigenschaft beschrieben werden soll.</param>
		/// <param name="propertyInfo">Die Informationen zur Eigenschaft die beschrieben werden soll.</param>
		public CompiledPropertyDescriptor(Type componentType, PropertyInfo propertyInfo)
			: this (componentType, propertyInfo, propertyInfo.Name) { }

		/// <summary>
		/// Erstellt eine neue Instanz der <see cref="CompiledPropertyDescriptor"/>-Klasse.
		/// </summary>
		/// <param name="componentType">Typ der Komponente deren Eigenschaft beschrieben werden soll.</param>
		/// <param name="propertyInfo">Die Informationen zur Eigenschaft die beschrieben werden soll.</param>
		/// <param name="name">Der Name, der als Name der Komponente gesetzt werden soll.</param>
		public CompiledPropertyDescriptor(Type componentType, PropertyInfo propertyInfo, string name)
			: base(componentType, propertyInfo, name)
		{
			if (componentType.IsNested || componentType.IsNotPublic)
			{
				Accessor = new Reflection.Dynamic.ReflectionFieldAccessor(propertyInfo);
				System.Diagnostics.Debug.WriteLine(
					String.Format("ATTENTION: The Type {0} was either declared nested or non-public."
								 +"That means that the related PropertyDescriptors such as this one will use ordinary and slow reflection to access the properties.", 
								 componentType.FullName));
			}
			else
				Accessor = DynamicFieldAccessor.Create(propertyInfo, false);
		}

		#endregion

		#region Methods

		/// <summary>
		/// Ruft den Wert der beschriebenen Eigenschaft aus der Komponente ab.
		/// </summary>
		/// <param name="component">Die Komponente mit der Eigenschaft, deren Wert abgerufen werden soll.</param>
		/// <returns>
		/// Der Wert der beschriebenen Eigenschaft für die angegebene Komponente.
		/// </returns>
		public override object GetValue(object component)
		{
			if (component == null)
				throw new ArgumentNullException("component");
			if (component is IObjectProxy)
			{
				IObjectProxy proxy = component as IObjectProxy;
				return Accessor.Get(proxy.ProxiedObject);
			}
			else
			{
				return Accessor.Get(component);
			}
		}

		/// <summary>
		/// Legt den Wert der beschriebenen Eigenschaft in der Komponente fest.
		/// </summary>
		/// <param name="component">Die Komponente mit dem Eigenschaftswert, der festgelegt werden soll.</param>
		/// <param name="value">Der neue Wert der Eigenschaft in der Komponente.</param>
		public override void SetValue(object component, object value)
		{
			if (component == null)
				throw new ArgumentNullException("component");
			if (component is IObjectProxy)
			{
				IObjectProxy proxy = component as IObjectProxy;
				Accessor.Set(proxy.ProxiedObject, value);
			}
			else
			{
				Accessor.Set(component, value);
			}
			base.OnValueChanged(component, new System.ComponentModel.PropertyChangedEventArgs(Name));
		}

		#endregion
	}
}
